Let's review the basics of objects covered in our previous lesson on objects. Recall we can create objects using either literal notation or constructor notation.
- Literal notation creates a single object. Literal notation uses curly brackets
{ }
and the object's default properties are defined within the brackets using property:value
notation. - Constructor notation involves defining an object constructor. And like defining a function, we use the
function
keyword. You can think of this constructor as a "template" from which you can create multiple objects. To create a new object from a constructor, we use the new
keyword.
Finish the james
object by adding properties to it. His job
should be"programmer"
and should have amarried
property set to false
.
Create a new gabby
object using thePerson
constructor. She should have ajob
of "student"
and her married
property should be true
.
var james = {
job: "programmer",
married: false
};
function Person(job, married) {
this.job = job;
this.married = married;
}
var gabby = new Person("student", true)
Recall that we can add methods (i.e., functions associated with objects) to a constructor:
function someObject() {
this.someMethod = function() {
};
}
Suppose we said var someObj = newsomeObject();
. When we callsomeObj.someMethod()
, the code between the curly brackets { }
above will run.
Add a speak
method to the Person
constructor. Whenever speak
is called, it should print "Hello!"
to the console.
function Person(job, married) {
this.job = job;
this.married = married;
this.speak = function() {
console.log("Hello!");
}
}
var user = new Person("Codecademy Student",false);
user.speak();
In the last exercise, we added methods to objects via constructor notation. We can also add methods to objects in literal notation:
var someObj = {
aProperty: value,
someMethod: function(some, params) { }
};
When we call someObj.someMethod(some,values);
, the code between the curly brackets { }
will run.
Note here we see a method that takes parameters. Methods defined in both constructors and literal notation can take parameters, just like normal functions.
Take a look at the partially-definedjames
object. Complete the speak
method such that the last two lines in the editor will cause "Hello, I am feeling great"
and "Hello, I am feeling just okay"
to be printed to the console.
var james = {
job: "programmer",
married: false,
speak: function(mood) {
console.log("Hello, I am feeling " + mood);
}
};
james.speak("great");
james.speak("just okay");
Can I See Your References?
Remember when defining a method for an object, it's easy to reference other properties in that object: just usethis.propertyName
!
When that method is called,this.propertyName
will always refer to the most recent value of propertyName
Take a look at the james
object. Complete the sayJob
method so that it will print to the console "Hi, I work asa [job]"
, where [job] is the value of thejob
property.
Then in line 14, change the job
forjames
to "super programmer"
. Although the method calls in lines 11 and 17 are exactly the same, their output should be different because James' job
changed!
var james = {
job: "programmer",
married: false,
sayJob: function() {
console.log("Hi, I work as a " + this.job)
}
};
james.sayJob();
james.job = "super programmer"
james.sayJob();
And finally, let's go over retrieving property values. Throughout this section, we've been using dot notation to get the value of an object's property:
However, remember that we can also usebracket notation:
An advantage of bracket notation is that we are not restricted to just using strings in the brackets. We can also use variables whose values are property names:
var someObj = {propName: someValue};
var myProperty = "propName";
someObj[myProperty];
The last line is exactly the same as usingsomeObj["propName"];
.
Take advantage of the ability to use variables with bracket notation.
In line 7, set aProperty
to a string of the first property in james
(ie. the job
property).
Then print james
's job
using bracket notation and aProperty
.
var james = {
job: "programmer",
married: false
};
var aProperty = ["job"];
console.log(james[aProperty]);
Alright! Let's get our hands dirty and start exploring some really cool stuff about objects in JavaScript. But before we can do that, how can we even tell if something is an object (as opposed to, say, a number or string)? It would be great if we could tell what typesomething is in JavaScript. Good thing there's a handy built-in operator to do this!
Say we have a variable thing
and we don't know what type thing
is. We can call typeof thing
to figure this out. Generally, the most useful types are "number," "string," "function," and of course, "object."
As an example, the following example will print "object"
:
var someObject = {someProperty: someValue};
console.log( typeof someObject );
In lines 3-6, we have an object, a number, and a string (in that order). Completelines 7-9 so they will print the appropriate types of these variables.
var anObj = { job: "I'm an object!" };
var aNumber = 42;
var aString = "I'm a string!";
console.log( typeof anObj );
console.log( typeof aNumber );
console.log( typeof aString );
In the last exercise, we used typeof
to figure out what type a variable in JavaScript is. Since we know how to tell objects apart from everything else now, let's focus on them.
You wouldn't know it, but every object in JavaScript comes with some baggage (stay tuned for more on this!). Part of this baggage includes a method calledhasOwnProperty
. This lets us know if an object has a particular property.
We show how to use hasOwnProperty
in the last two lines. It returns true
orfalse
, based on whether an object has a certain property.
You should finish myObj
by giving it aname
property. Make sure that myObj
does not have a nickname
property so that the last line will print false
.
var myObj = {
name: "Jesse"
};
console.log( myObj.hasOwnProperty('name') );
console.log( myObj.hasOwnProperty('nickname') );
Let's get some practice working withhasOwnProperty
. It is an invaluable tool when working with objects!
Try to run the code in the editor as it is. You should get an error because shorts
is not a property of the suitcase
object.
Let's write some code to test for this so we can avoid this nasty error later
Remove the console.log
statement.
Write an if
statement that checks to see if suitcase
has the shorts
property.
If your if
statement evaluates to true
, print the value of the shorts
property.
If your if
statement evaluates tofalse
, set the shorts
property to any value you wish using dot notation. Then print the value of the shorts
property
var suitcase = {
shirt: "Hawaiian"
};
if (suitcase.hasOwnProperty('shorts')){
console.log(suitcase.shorts);
}
else
{
suitcase.shorts = "red";
console.log(suitcase.shorts);
}
Now let's learn how to work with all the properties that belong to an object. First, let's define an object:
var dog = {
species: "bulldog",
age: 3,
color: brown
};
To print out all elements, we can use afor
/in
loop, like this:
for(var property in dog) {
console.log(property);
}
In the loop we use console.log
to print out each key. Remember the "property" bit can be any placeholder name you like.
var nyc = {
fullName: "New York City",
mayor: "Bill de Blasio",
population: 8000000,
boroughs: 5
};
for (var x in nyc){
console.log(x);
}
We've just seen how to print all of an object's property names with a for-in loop. But how do we print out all the values associated with every property? Surprise! The for-in loop will be our friend again! Let's get there slowly. Our dog object can help us.
var dog = {
species: "bulldog",
age: 3,
color: brown
};
First, remember that
dog.species = dog["species"] = "bulldog";
And if we say:
var x = "species";
then
dog[x] = "bulldog";
We see that by assigning the property name to a variable, we can then use the variable name in bracket notation to get the property's value. So to get all the values from the dog object, we would use the for-in loop and the bracket notation we just saw above. See the hint to see the code to print the property values fordog
.
var nyc = {
fullName: "New York City",
mayor: "Bill de Blasio",
population: 8000000,
boroughs: 5
};
for (var x in nyc){
console.log(nyc[x]);
}
Alright, it's time to learn the basics ofobject-oriented programming! Often abbreviated OOP, this is a very important programming paradigm that is widely used in the industry today.
Let's start by introducing classes. We learned in the last course that constructors are a way to make objects, but they actually do even more than that.
When you make a constructor, you are in fact defining a new class. A class can be thought of as a type, or a category of objects—kind of like how Number
andString
are types in JavaScript.
Take a look at our Person
example taken from Introduction to Objects I. In this case bob
and susan
are two separate objects, but both belong to the classPerson
.
Make your own class, Circle
, by building a constructor for it. The constructor for Circle
should have one property, radius
, and take one argument for the initial radius
function Person(name,age) {
this.name = name;
this.age = age;
}
var bob = new Person("Bob Smith", 30);
var susan = new Person("Susan Jordan", 35);
function Circle(radius) {
this.radius = radius;
}
So we know that a class will have certain properties and methods, but what keeps track of what a given class can or can't do? What a class has or doesn't have? That is the job of the prototype.
JavaScript automatically defines the prototype for class with a constructor. For example, our Dog
constructor ensures that the Dog
prototype has a breed
property. Remember, the Dog
prototype keeps track of what Dog
has, doesn't have, can, or can't do.
We know we can add methods to objects, and in line 7 we add the bark
method tobuddy
. Hit run and you will see one"Woof"
printed when buddy
barks. Notice what happens when we try to get snoopy
to bark in line 17 though. Even though snoopy is of the class Dog
, he doesn't know how to bark because only buddy
hadbark
added as a method.
function Dog (breed) {
this.breed = breed;
}
var buddy = new Dog("Golden Retriever");
buddy.bark = function() {
console.log("Woof");
};
buddy.bark();
var snoopy = new Dog("Beagle");
snoopy.bark = function() {
console.log("Snoopy barks!");
}
snoopy.bark();
Classes are very important in object-oriented programming. This is because a class tells us helpful information about objects, and you can think of an object as a particular instance of a class.
For example, look at our Person
class again in the console. We know that anyPerson
will have a name
and age
, because they are in the constructor. This allows us to create a function likeprintPersonName
, which will take a Person
as an argument and print out their name. We know the function will work on anyPerson
, because name
is a valid property for that class.
Make a Person
called me
with your own name and age, and print your name usingprintPersonName
function Person(name,age) {
this.name = name;
this.age = age;
}
var printPersonName = function (p) {
console.log(p.name);
};
var bob = new Person("Bob Smith", 30);
printPersonName(bob);
var me = new Person("Jesse Menkin", 32);
printPersonName(me);
Here we have very similar code as last time, but there is an important difference. Instead of using buddy.bark
to add the bark method to just the buddy
object, we use Dog.prototype.bark
.
Click run this time, and both buddy
andsnoopy
can bark just fine! Snoopy
can bark too even though we haven't added a bark method to that object. How is this so? Because we have now changed theprototype for the class Dog
. This immediately teaches all Dog
s the new method.
In general, if you want to add a method to a class such that all members of the class can use it, we use the following syntax toextend the prototype:
className.prototype.newMethod =
function() {
statements;
};
function Dog (breed) {
this.breed = breed;
};
var buddy = new Dog("golden Retriever");
Dog.prototype.bark = function() {
console.log("Woof");
};
buddy.bark();
var snoopy = new Dog("Beagle");
snoopy.bark();
Here we have created a new class, Cat
, and its constructor. We also have two cats that would like to meow, but currentlyCat
s have no meow
method.
Add a meow
method to the Cat
prototype so that all cats can now meow. This method should print to the console "Meow!"
. Then call this method for each cat.
function Cat(name, breed) {
this.name = name;
this.breed = breed;
}
var cheshire = new Cat("Cheshire Cat", "British Shorthair");
var gary = new Cat("Gary", "Domestic Shorthair");
Cat.prototype.meow = function() {
console.log("Meow!");
}
cheshire.meow();
gary.meow();
In object-oriented programming,inheritance allows one class to see and use the methods and properties of another class. You can think of it as a child being able to use his or her parent's money because the child inherits the money.
We will learn more about inheritance as we continue this lesson, but for now let's just refresh our memories about how classes and objects work.
Create a class named Animal
with two properties, name
and numLegs
. TheAnimal
constructor should have two arguments whose values are assigned toname
and numLegs
.
Next, change the prototype
of Animal
and add a method sayName
that prints to the console "Hi my name is [name]"
, where[name] is the value of name
.
Click "Stuck? Get a hint!" for examples of how to create a class and how to add a method to an object's prototype.
Finally, we have provided the last two lines to test your constructor and sayName
method. Don't change these!
function Animal(name, numLegs) {
this.name = name;
this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
console.log("Hi my name is " + this.name);
}
var penguin = new Animal("Captain Cook", 2);
penguin.sayName();
HINT:
Remember how we created a Person
class?
function Person(name,age) {
this.name = name;
this.age = age;
};
Also recall how we added a method to a class's prototype
:
Dog.prototype.bark = function() {
console.log("Woof");
};
To reference an Animal
's name
property when changing its prototype, be sure to use this.name
.
Remember when we want to insert the value of a variable in a String, we use the+
operator:
var num = 3;
var str = "The number is " + num!";
Let's say we're dealing with a lot of Penguins. It sure would be nice to create aPenguin
class so that perhaps later we can give it some methods unique to a penguin and not confuse it with the Animal
class.
Create a brand new Penguin
class constructor starting in line 11. A penguin is an animal so it should also have the name
and numLegs
properties as well as asayName
method that prints the same thing as Animal
's sayName
method.
We're not done with animals yet, so we have still included the Animal
constructor and its sayName
method. The last two lines test your Penguin
code.
function Animal(name, numLegs) {
this.name = name;
this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
console.log("Hi my name is " + this.name);
};
function Penguin(name, numLegs) {
this.name = name;
this.numLegs = numLegs;
}
Penguin.prototype.sayName = function(){
console.log("Hi my name is " + this.name);
}
var theCaptain = new Penguin("Captain Cook", 2);
theCaptain.sayName();
Creating a brand new Penguin
was nice, but we did end up reusing a lot of the same code as the Animal
class. This goes against the "DRY" principle of programming: Don't Repeat Yourself.
Inheritance can help us here! A Penguin
is an Animal
, so they should have all the same properties and methods as Animal
. Whenever this X is-a Y relationship exists, there's a good chance that we should be using inheritance.
Remember, inheritance lets us see and use properties and methods from another class. To say that Penguin
inherits fromAnimal
, we need to set Penguin
'sprototype
to be Animal
.
Create a new Penguin
class. The Penguin
constructor can be more unique than the generic Animal
one because all penguins have 2 legs. Your constructor should only take a name
parameter, and within the constructor itself, set this.numLegs
to 2
.
Set the Penguin
class's prototype
to a new instance of Animal
by adding this line after you make the constructor:
Penguin.prototype = new Animal();
This means that Penguin
inheritsproperties and methods from Animal
.
function Animal(name, numLegs) {
this.name = name;
this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
console.log("Hi my name is " + this.name);
};
function Penguin(name) {
this.name = name;
this.numLegs = 2;
}
Penguin.prototype = new Animal();
Black (and White) Penguin Magic
Now for some black magic and to see the power of inheritance!
We never defined a sayName
method forPenguin
, but what happens when we try to call it?
Create a Penguin
object with the variable name penguin
and any name you'd like.
Then call penguin.sayName();
.
Then be amazed.
function Animal(name, numLegs) {
this.name = name;
this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
console.log("Hi my name is " + this.name);
};
function Penguin(name) {
this.name = name;
this.numLegs = 2;
}
Penguin.prototype = new Animal();
var penguin = new Penguin("Bob");
penguin.sayName();
Penguins, Properties, and the Prototype
We saw in the last exercise how Penguin
inherited the sayName
method fromAnimal
. We now explore how classes can inherit properties as well.
For simplicity, we've defined a newPenguin
class that doesn't inherit anything from Animal
Create an Emperor
class that takes a singlename
parameter and sets its name
property to be this value. Don't set anumLegs
property in the constructor.
Similar to what we did in the previous exercise, make Emperor
inherit fromPenguin
by setting the prototype
ofEmperor
to be Penguin
.
Create a new emperor
object that is an instance of the Emperor
class with any name you'd like. Then use console.log
to print the number of legs emperor
has—this should have been inherited from Penguin
function Penguin(name) {
this.name = name;
this.numLegs = 2;
}
function Emperor(name) {
this.name = name;
}
Emperor.prototype = new Penguin();
var emperor = new Emperor("Joe");
console.log(emperor.numLegs);
Up the Food-I-mean-Prototype Chain
A penguin is an animal and an emperor penguin is a penguin. Are emperor penguins animals too? Of course!
The "prototype chain" in JavaScript knows this as well. If JavaScript encounters something it can't find in the current class's methods or properties, it looks up theprototype chain to see if it's defined in a class that it inherits from. This keeps going upwards until it stops all the way at the top: the mighty Object.prototype
(more on this later). By default, all classes inherit directly from Object
, unless we change the class's prototype
, like we've been doing for Penguin
and Emperor
.
Let's see how going up the prototype chain works! We've defined some classes and inheritance patterns: Emperor
inherits from Penguin
which inherits from Animal
. We've also created an instance of theEmperor
class.
Without modifying anything other thanlines 22-24, complete the console.log
statements to print the appropriate responses.
Remember how the prototype chain works: if a property is not defined for a class, this class's prototype chain will be traversed upwards until one is found (or not) in a parent (higher) class.
function Animal(name, numLegs) {
this.name = name;
this.numLegs = numLegs;
this.isAlive = true;
}
function Penguin(name) {
this.name = name;
this.numLegs = 2;
}
function Emperor(name) {
this.name = name;
this.saying = "Waddle waddle";
}
Penguin.prototype = new Animal();
Emperor.prototype = new Penguin();
var myEmperor = new Emperor("Jules");
console.log(myEmperor.saying);
console.log(myEmperor.numLegs);
console.log(myEmperor.isAlive);
In JavaScript all properties of an object are automatically public. Public means that they can be accessed outside the class. Think of these properties as the information a class is willing to share.
Look at the Person
class. It has 3 public properties: firstName
, lastName
, andage
. On lines 8 and 9, we access thefirstName
and lastName
properties ofjohn
and assign them to myFirst
andmyLast
.
Notice that we are free to access thefirstName
and lastName
properties, which is what we mean when we say they are public.
Declare a third variable called myAge
and use it to store the age property of the john
object.
function Person(first,last,age) {
this.firstName = first;
this.lastName = last;
this.age = age;
}
var john = new Person('John','Smith',30);
var myFirst = john.firstName;
var myLast = john.lastName;
var myAge = john.age
Good! But what if an object wants to keep some information hidden?
Just as functions can have local variables which can only be accessed from within that function, objects can have private variables. Private variables are pieces of information you do not want to publicly share, and they can only be directly accessed from within the class.
The Person
class has been modified to have a private variable called bankBalance
. Notice that it looks just like a normal variable, but it is defined inside the constructor for Person
without usingthis
, but instead using var
. This makesbankBalance
a private variable.
Create an object john
using the Person
constructor. He can have any name and age you wish.
Next, try to print his bankBalance
. What happens?
function Person(first,last,age) {
this.firstname = first;
this.lastname = last;
this.age = age;
var bankBalance = 7500;
}
var john = new Person("john", "johnson", 21);
console.log(john.bankBalance)
Accessing Private Variables
Although we cannot directly access private variables from outside the class, there is a way to get around this. We can define a public method that returns the value of a private variable.
Here we have included similar code from last time, but here we have added a method getBalance
. Modify getBalance
so that it returns bankBalance
.
Then on line 17, create a new variable named myBalance
and set its value to John's bank balance. You can do this by calling your newly-defined getBalance
method for john
. Then print myBalance
.
Line 14 should still print undefined
!
function Person(first,last,age) {
this.firstname = first;
this.lastname = last;
this.age = age;
var bankBalance = 7500;
this.getBalance = function() {
return bankBalance;
};
}
var john = new Person('John','Smith',30);
console.log(john.bankBalance);
var myBalance = john.getBalance();
console.log(myBalance)
Why did that code work? An object's private variables can only be accessed by other methods that are part of that same object. So, we just used an object's public method to access a private variable!
Methods can also be private within a class and inaccessible outside of the class. Changing this.returnBalance
from the last exercise to var returnBalance
makes this method private. If you run the program trying to access the method you get anundefined
error this time.
The way to access a private method is similar to accessing a private variable. You must create a public method for the class that returns the private method.
Create a method called askTeller
within the Person
class that returns thereturnBalance
method. This means that it returns the method itself and NOT the result of calling that method. So you shouldNOT have parentheses afterreturnBalance
.
Because askTeller
returns a method, we need to call it to make it any use. This is what var myBalance = myBalanceMethod();
does.
function Person(first,last,age) {
this.firstname = first;
this.lastname = last;
this.age = age;
var bankBalance = 7500;
var returnBalance = function() {
return bankBalance;
};
this.askTeller = function(){
return returnBalance;
}
}
var john = new Person('John','Smith',30);
console.log(john.returnBalance);
var myBalanceMethod = john.askTeller();
var myBalance = myBalanceMethod();
console.log(myBalance);
The askTeller
function has been modified within the Person
class to directly give you your balance. However, it now needs the account password in order to return thebankBalance
Create a new variable called myBalance
that calls the askTeller
function with a password argument, 1234
function Person(first,last,age) {
this.firstname = first;
this.lastname = last;
this.age = age;
var bankBalance = 7500;
this.askTeller = function(pass) {
if (pass == 1234) return bankBalance;
else return "Wrong password.";
};
}
var john = new Person('John','Smith',30);
var myBalance = john.askTeller(1234);
Objects aren't so foreign if you really think about it!
Remember you can figure out the type of a variable by using typeof myVariable;
. Types we are concerned with for now are"object"
, "string"
, and "number"
.
Recall the for
-in
loop:
for(var x in obj) {
executeSomething();
}
This will go through all the properties ofobj
one by one and assign the property name to x
on each run of the loop.
Let's combine our knowledge of these two concepts.
Examine the languages
object. Three properties are strings, whereas one is a number.
Use a for
-in
loop to print out the three ways to say hello. In the loop, you should check to see if the property value is a string so you don't accidentally print a number
var languages = {
english: "Hello!",
french: "Bonjour!",
notALanguage: 4,
spanish: "Hola!"
};
for(var x in languages) {
if (typeof(languages[x]) == 'string') {
console.log(languages[x]);
}
}
We should all know by now what's so cool about using prototype
: we can define a method for a class, and any instance of the class (i.e., object created using that class's constructor) can use that method.
Remember that classes and the prototype are important to OOP!
Add the sayHello
method to the Dog
class by extending its prototype.
sayHello
should print to the console:"Hello this is a [breed] dog"
, where[breed] is the dog's breed.
function Dog (breed) {
this.breed = breed;
};
Dog.prototype.sayHello = function(){
console.log("Hello this is a " + this.breed + " dog")
}
var yourDog = new Dog("golden retriever");
yourDog.sayHello();
var myDog = new Dog("dachshund");
myDog.sayHello();
Do you remember how we said every JavaScript object has some baggage associated with it? Part of this baggage was the hasOwnProperty
method available to all objects. Now let's see where this came from...
If we have just a plain object (i.e., not created from a class constructor), recall that it automatically inherits fromObject.prototype
. Could this be where we get hasOwnProperty
from? How can we check?
Let's first see what type Object.prototype
is. Do this in line 2 and save it intoprototypeType
.
If all goes well, you should realize thatObject.prototype
itself is an object! And since all objects have the hasOwnProperty
method, it's pretty easy to check ifhasOwnProperty
comes fromObject.prototype
. Do this in line 6 and the result may be surprising.
var prototypeType = typeof Object.prototype;
console.log(prototypeType);
var hasOwn = Object.prototype.hasOwnProperty( "hasOwnProperty");
console.log(hasOwn);
Recall that:
- Public properties can be accessed from outside the class
- Private properties can only be accessed from within the class
Using constructor notation, a property declared as this.property ="someValue;"
will be public, whereas a property declared with var property ="hiddenValue;"
will be private.
In this exercise, hit run and you'll see that all your grades are exposed! You really just want people to know your overall GPA.
Modify the StudentReport
class so that no grades will be printed to the console in thefor
-in
loop.
However, getGPA
should still function properly in the last line.
function StudentReport() {
var grade1 = 4;
var grade2 = 2;
var grade3 = 1;
this.getGPA = function() {
return (grade1 + grade2 + grade3) / 3;
};
}
var myStudentReport = new StudentReport();
for(var x in myStudentReport) {
if(typeof myStudentReport[x] !== "function") {
console.log("Muahaha! " + myStudentReport[x]);
}
}
console.log("Your overall GPA is " + myStudentReport.getGPA());